import got from 'got';
import * as moment from 'moment';
import * as _ from 'lodash';
import * as path from 'path';
import * as fs from 'fs';
import * as utils from '@/utils';
import { type Product } from '@/product/product.mode';

interface Order extends Omit<Product, 'keyword'> {
  keyword: string;
  alert?: boolean;
}

/**
 * 一组内产品总金额最大值
 */
const MAX_GROUP_TOTAL_MONEY = 1800;

/**
 * 随机取行index
 */
function createDataRowIndex(length: number, pre: number): number {
  if (length < 2) return 0;

  const index = Math.floor(Math.random() * length);

  if (index !== pre) {
    return index;
  }

  return createDataRowIndex(length, pre);
}

function createProduct(product: Product[]) {
  return product.map((item) => ({
    ...item,
    keyword: item.keyword.reduce(
      (previousValue, currentValue) =>
        previousValue.concat(Array(currentValue.num).fill(currentValue.value)),
      [] as string[],
    ),
  }));
}

/**
 * 根据产品创建订单
 */
export function createOrder(product: Product[]): Order[][] {
  const data = createProduct(product);

  return data.map((item) => {
    const result = item.keyword.map((keyword) => {
      const data = { ...item, keyword };

      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      delete data._id;
      return data;
    });

    const price = result.reduce((pre, cur) => pre + cur.price, 0);

    if (price > MAX_GROUP_TOTAL_MONEY) {
      result.forEach((item) => {
        Object.defineProperty(item, 'alert', {
          value: true,
        });
      });
    }

    return result;
  });
}

/**
 * 根据今日总单数分组
 *
 * sortType
 *
 * 类型1: `noStore` 同店铺产品不能在一组内
 *
 * 类型2: `noBoss` 同老板店铺不能在一组内
 *
 * 类型3: `limit` 同老板最多三个不同店铺在一组内
 */
export function hashOrder(order: Order[][], rowSize: number) {
  const data = _.cloneDeep(order);
  /** 打散订单组 */
  const response: (Order | null)[][] = [];
  /** 当前行下标 */
  let currentRowIndex = 0;
  /** 上一行下标 */
  let preRowIndex = 0;
  /**
   * 回收的订单
   */
  const recovery: Order[] = [];

  /**
   * 随机插入
   */
  while (data.length) {
    const currentDataRowIndex = createDataRowIndex(data.length, preRowIndex);
    preRowIndex = currentDataRowIndex;

    /**
     * 删除已分配的产品组
     */
    if (!data[currentDataRowIndex].length) {
      data.splice(currentDataRowIndex, 1);
      continue;
    }

    if (!Array.isArray(response[currentRowIndex])) {
      currentRowIndex = response.push([]) - 1;
    }

    /**
     * 当一组内产品已分配满
     */
    if (response[currentRowIndex].length >= rowSize) {
      currentRowIndex += 1;
      continue;
    }

    const [order] = data[currentDataRowIndex].splice(0, 1);

    response[currentRowIndex].push(order);
  }

  /**
   * 去重回收♻️
   */
  response.forEach((row) => {
    let delNum = 0;
    let breakWhile = false;

    while (true) {
      for (let i = 0; i < row.length; i++) {
        const bossNames = row.map((item) => item!.boss);

        /**
         * 如果有重复的产品 回收 并且 插入 null
         */
        const hasIndex = bossNames.indexOf(row[i]!.boss, 0);
        if (hasIndex !== i) {
          const [del] = row.splice(i, 1);
          recovery.push(del!);
          delNum += 1;
          break;
        }

        breakWhile = i >= row.length - 1;
      }

      if (breakWhile) break;
    }

    while (delNum) {
      row.push(null);
      delNum--;
    }
  });

  response.forEach((row) => insertOrder(row, recovery));

  /**
   * 处理剩余已经没办法再分配的订单
   */
  if (recovery.length) {
    Array.prototype.push.apply(response, utils.chunk(recovery, rowSize));
  }

  console.log('response');
  console.log(response.length);
  console.log('response-end');
  console.log(recovery);
  /**
   * 导出本次数据到json
   */
  exportJSON(response);
  return response;
}

/**
 * 返回 boss 店铺在 一组内数量
 */
function bossStoreCount(row: Order[], boss: string) {
  const result: Record<string, number> = Object.create(null);

  for (const item of row) {
    if (result[item.boss]) {
      result[item.boss]++;
    } else {
      result[item.boss] = 1;
    }
  }

  return result[boss];
}

/**
 * 判断当前产品是否能插入组内
 *
 * @param row 订单组
 * @param order 订单
 */
function productInGroup(row: (Order | null)[], order: Order) {
  const data = row.filter(Boolean) as Order[];
  const storeIds: string[] = [];
  const bossNames: string[] = [];

  for (const item of data) {
    bossNames.push(item.boss);
    storeIds.push(item.storeId);
  }

  switch (order.sortType) {
    case 'noStore':
      return storeIds.indexOf(order.storeId) > -1;
    case 'noBoss':
      return bossNames.indexOf(order.boss) > -1;
    case 'limit':
      if (storeIds.indexOf(order.storeId) > -1) return true;
      return bossStoreCount(data, order.boss) >= 3;
  }
}

/**
 * 查找当前组内是否有null
 */
function hasNull(data: (Order | null)[]) {
  return data.filter((item) => !Boolean(item)).length > 0;
}

/**
 *
 * @param data 当前订单组
 * @param recovery 回收的重复订单
 * @param loop 当前检查次数
 */
function insertOrder(row: (Order | null)[], recovery: Order[], loop = 0) {
  if (hasNull(row)) {
    /**
     * ! break
     * 仅检查一次 如果还有null 放到下面递归检查
     */
    for (let i = 0; i < recovery.length; i++) {
      if (!productInGroup(row, recovery[i])) {
        const [item] = recovery.splice(i, 1);
        /** 必定返回 index > -1 */
        const insertIndex = row.indexOf(null);
        row[insertIndex] = item;
        break;
      }
    }
  }

  /**
   * 如果检查次数 < 组原素长度 且 依然有 null 继续检查
   */
  if (hasNull(row) && loop < row.length) insertOrder(row, recovery, loop + 1);
}

async function exportJSON(data: unknown) {
  const filePath = path.resolve('data.log');

  const value = `[${moment().format('YYYY-MM-DD HH:mm:ss')}]\r${JSON.stringify(
    data,
    null,
    4,
  )}\r`;

  try {
    await fs.appendFileSync(filePath, value);
    console.log('The file was saved!');
  } catch (err) {
    console.error(err);
  }
}

export function downloadImage(data: Product[]): Promise<Buffer[]> {
  return new Promise((resolve, reject) => {
    const buffer: Buffer[] = [];
    let promise: Promise<number | void> = Promise.resolve();

    function queue(index: number) {
      if (index > data.length - 1) {
        resolve(buffer);
        return;
      }

      promise = promise.then(() => {
        return got
          .get({
            url: data[index].image,
            headers: {
              accept:
                'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
              'accept-language': 'zh-CN,zh;q=0.9',
              'user-agent':
                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36',
            },
          })
          .buffer()
          .then((response) => {
            buffer.push(response);
            queue(index + 1);
          })
          .catch((err) => {
            // ! 出错后 等待一秒后 重新开始下载
            console.error(err);
            setTimeout(() => {
              queue(index);
            }, 1000);
          });
      });
    }

    queue(0);
  });
}

/**
 *
 * @example
 * d = [
    [{name: '1-1'}, {name: '1-2'}],
    [{name: '2-1'}, {name: '2-2'}],
    [{name: '3-1'}, {name: '3-2'}, {name: '3-3'}]
  ]
  =>
  r = [
    [{name: '1-1'}, {name: '2-1'}, {name: '3-1'}],
    [{name: '1-2'}, {name: '2-2'}, {name: '3-2'}],
    [{name: '3-3'}]
  ]
 */
export function renderSheet(data: (Order | null)[][]) {
  const result: (Order | null)[][] = [];
  let i = 0;

  while (true) {
    const temp: (Order | null)[] = [];

    for (const group of data) {
      temp.push(group[i]);
    }

    if (!temp.some(Boolean)) break;

    result.push(temp);

    i++;
  }

  return result;
}
